use std::os::raw::{c_int, c_char};
use std::ptr;

use ratatui::layout::{Constraint, Direction, Layout, Rect};
use crate::{RatatuiResult, c_str_to_string};

/// Rectangle structure for layout
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RatatuiRect {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

impl RatatuiRect {
    pub fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
        RatatuiRect { x, y, width, height }
    }

    pub fn area(&self) -> u32 {
        self.width as u32 * self.height as u32
    }

    pub fn is_empty(&self) -> bool {
        self.width == 0 || self.height == 0
    }
}

impl From<ratatui::layout::Rect> for RatatuiRect {
    fn from(rect: ratatui::layout::Rect) -> Self {
        RatatuiRect {
            x: rect.x,
            y: rect.y,
            width: rect.width,
            height: rect.height,
        }
    }
}

impl From<RatatuiRect> for ratatui::layout::Rect {
    fn from(rect: RatatuiRect) -> Self {
        ratatui::layout::Rect {
            x: rect.x,
            y: rect.y,
            width: rect.width,
            height: rect.height,
        }
    }
}

/// Create a new rectangle
#[no_mangle]
pub extern "C" fn ratatui_rect_new(x: u16, y: u16, width: u16, height: u16) -> RatatuiRect {
    RatatuiRect::new(x, y, width, height)
}

/// Get rectangle area
#[no_mangle]
pub extern "C" fn ratatui_rect_area(rect: RatatuiRect) -> u32 {
    rect.area()
}

/// Check if rectangle is empty
#[no_mangle]
pub extern "C" fn ratatui_rect_is_empty(rect: RatatuiRect) -> bool {
    rect.is_empty()
}

/// Check if point is inside rectangle
#[no_mangle]
pub extern "C" fn ratatui_rect_contains(rect: RatatuiRect, x: u16, y: u16) -> bool {
    x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height
}

/// Get intersection of two rectangles
#[no_mangle]
pub extern "C" fn ratatui_rect_intersect(rect1: RatatuiRect, rect2: RatatuiRect) -> RatatuiRect {
    let x1 = rect1.x.max(rect2.x);
    let y1 = rect1.y.max(rect2.y);
    let x2 = (rect1.x + rect1.width).min(rect2.x + rect2.width);
    let y2 = (rect1.y + rect1.height).min(rect2.y + rect2.height);

    if x2 > x1 && y2 > y1 {
        RatatuiRect::new(x1, y1, x2 - x1, y2 - y1)
    } else {
        RatatuiRect::new(0, 0, 0, 0)
    }
}

/// Get union of two rectangles
#[no_mangle]
pub extern "C" fn ratatui_rect_union(rect1: RatatuiRect, rect2: RatatuiRect) -> RatatuiRect {
    if rect1.is_empty() {
        return rect2;
    }
    if rect2.is_empty() {
        return rect1;
    }

    let x1 = rect1.x.min(rect2.x);
    let y1 = rect1.y.min(rect2.y);
    let x2 = (rect1.x + rect1.width).max(rect2.x + rect2.width);
    let y2 = (rect1.y + rect1.height).max(rect2.y + rect2.height);

    RatatuiRect::new(x1, y1, x2 - x1, y2 - y1)
}

/// Layout direction
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RatatuiDirection {
    Horizontal = 0,
    Vertical = 1,
}

impl From<RatatuiDirection> for Direction {
    fn from(dir: RatatuiDirection) -> Self {
        match dir {
            RatatuiDirection::Horizontal => Direction::Horizontal,
            RatatuiDirection::Vertical => Direction::Vertical,
        }
    }
}

/// Layout constraint type
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RatatuiConstraintType {
    Percentage = 0,
    Length = 1,
    Min = 2,
    Max = 3,
}

/// Layout constraint
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct RatatuiConstraint {
    pub constraint_type: RatatuiConstraintType,
    pub value: u16,
}

impl From<RatatuiConstraint> for Constraint {
    fn from(constraint: RatatuiConstraint) -> Self {
        match constraint.constraint_type {
            RatatuiConstraintType::Percentage => Constraint::Percentage(constraint.value),
            RatatuiConstraintType::Length => Constraint::Length(constraint.value),
            RatatuiConstraintType::Min => Constraint::Min(constraint.value),
            RatatuiConstraintType::Max => Constraint::Max(constraint.value),
        }
    }
}

/// Layout handle for FFI
pub struct RatatuiLayout {
    direction: Direction,
    constraints: Vec<Constraint>,
    margin: u16,
}

impl RatatuiLayout {
    pub fn new(direction: Direction) -> Self {
        RatatuiLayout {
            direction,
            constraints: Vec::new(),
            margin: 0,
        }
    }

    pub fn add_constraint(&mut self, constraint: Constraint) {
        self.constraints.push(constraint);
    }

    pub fn set_margin(&mut self, margin: u16) {
        self.margin = margin;
    }

    pub fn split(&self, area: Rect) -> Vec<Rect> {
        let layout = Layout::default()
            .direction(self.direction)
            .constraints(self.constraints.clone())
            .margin(self.margin);

        layout.split(area).to_vec()
    }
}

/// Create a new constraint
#[no_mangle]
pub extern "C" fn ratatui_constraint_new(constraint_type: RatatuiConstraintType, value: u16) -> RatatuiConstraint {
    RatatuiConstraint {
        constraint_type,
        value,
    }
}

/// Create a new layout
#[no_mangle]
pub extern "C" fn ratatui_layout_new(direction: RatatuiDirection) -> *mut RatatuiLayout {
    let layout = RatatuiLayout::new(direction.into());
    Box::into_raw(Box::new(layout))
}

/// Free layout
#[no_mangle]
pub extern "C" fn ratatui_layout_free(layout: *mut RatatuiLayout) {
    if !layout.is_null() {
        unsafe {
            let _ = Box::from_raw(layout);
        }
    }
}

/// Add constraint to layout
#[no_mangle]
pub extern "C" fn ratatui_layout_add_constraint(layout: *mut RatatuiLayout, constraint: RatatuiConstraint) -> c_int {
    if layout.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    unsafe {
        let layout = &mut *layout;
        layout.add_constraint(constraint.into());
        RatatuiResult::Ok as c_int
    }
}

/// Set layout margin
#[no_mangle]
pub extern "C" fn ratatui_layout_set_margin(layout: *mut RatatuiLayout, margin: u16) -> c_int {
    if layout.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    unsafe {
        let layout = &mut *layout;
        layout.set_margin(margin);
        RatatuiResult::Ok as c_int
    }
}

/// Split area using layout
#[no_mangle]
pub extern "C" fn ratatui_layout_split(
    layout: *const RatatuiLayout,
    area: RatatuiRect,
    result_rects: *mut RatatuiRect,
    max_rects: usize,
) -> c_int {
    if layout.is_null() || result_rects.is_null() {
        return RatatuiResult::ErrorNullPointer as c_int;
    }

    unsafe {
        let layout = &*layout;
        let ratatui_area: Rect = area.into();
        let split_rects = layout.split(ratatui_area);

        let count = split_rects.len().min(max_rects);
        for (i, rect) in split_rects.iter().take(count).enumerate() {
            *result_rects.add(i) = (*rect).into();
        }

        count as c_int
    }
}
